#include "gdr.h"
#include "boost/variant.hpp"
#include "rectype.h"
#include "util.h"

#include "nmfield.h"
#include "nmfield.cpp"

#include "nmarrfield.h"
#include "nmarrfield.cpp"

#include "cnfield.h"
#include "cnfield.cpp"

#include "cnarrfield.h"

#include "chararray.h"

#include "gdrstdffield.h"
#include "gdrprintvisitor.h"

using namespace std;
using namespace opsrc::stdf;

Gdr::Gdr()
{}

Gdr::Gdr(stdf_rec _stdfrec, int _reclen, std::ios::pos_type _fileRecFirstBytePosition, int _orderindex, endian e, char* buf )
: RecBase( _stdfrec, _reclen, _fileRecFirstBytePosition, _orderindex),en(e) {
    if (buf != 0)
        readFields(buf);
}

Gdr::~Gdr() {
}

const StdfField<int> Gdr::getFieldCount() const {
    return fld_cnt;
}

void Gdr::setFieldCount(StdfField<int> fldcnt) {
    fld_cnt = fldcnt;
}


void Gdr::readFields(char *buf) {
#if _DEBUG
    cout << "stdfrec= GDR , filebytestartposition= " <<   getFileRecByteStartPosition() << ", recbytecount= " << reclen <<endl;
#endif
    int curpos = 0;
    // Read field count
    NmField<unsigned int, 2> fldcnt(buf+curpos, en);
    fld_cnt.set(fldcnt.getData(), curpos, fldcnt.getByteCount(), fldcnt.getTotalByteCount());
   // cout << "\tField_count=" << fld_cnt.getData() << " pos=" << fld_cnt.getOffset()  << " len=" << fld_cnt.getTotalByteCount() << endl;

    GdrPrintVisitor vis;

    curpos += fld_cnt.getTotalByteCount();

    for ( int i=0; i < fld_cnt.getData(); i++ ) {
        // exit clause
        if ( curpos > reclen -1) return;

        // curpos is the offset position for the next field to decode
        GdrStdfField *p = createDataField(buf, curpos);
        if (p!=NULL ) {
            // found valid field, push into container
            if (p->getGdrDataType() != B0 )
                cont.push_back(p);

            //string tmp;
            //gdr_variant x;

            // increment curpos position

            switch (p->getGdrDataType()) {
            case B0: {
                    //delete p;
                    //p = NULL;
                    curpos++;  // increment 1 position
                    break;
                }
            case U1:  {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-U1=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case U2: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-U2=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case U4: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-U4=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case I1: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-I1=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case I2: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-I2=" << tmp << " pos=" << p->getOffset() << " len="<< p->getTotalByteCount() <<  endl;
                    break;
                }
            case I4: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-I4=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case R4: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-R4=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case R8: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-R8=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case CN: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-CN=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case BN: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-BN=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case DN: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-DN=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case N1: {
                    curpos+= p->getTotalByteCount();
                    //x = p->getData();
                    //tmp = boost::apply_visitor(vis, x);
                    //cout << "\tF" << i << "-N1=" << tmp << " pos=" << p->getOffset() << " len=" << p->getTotalByteCount() <<  endl;
                    break;
                }
            case UK:
                break;

            };
        }

    }

}


gdr_data_typ Gdr::checkDataFieldType(int index) const {
    if (index <= (int) cont.size() ) {
        GdrStdfField* p = cont[index];
        return p->getGdrDataType();
    }
    else {
        return UK;
    }
}

GdrStdfField* Gdr::getGdrStdfField(int index) const {
    if (index <= (int) cont.size() ) {
        GdrStdfField* p = cont[index];
        return p;
    }
    else {
        return NULL;
    }

}



GdrStdfField* Gdr::createDataField(char* buf, int curpos) {

    gdr_data_typ val = checkGdrFieldType(buf+curpos);
    GdrStdfField* ps = NULL;

    switch (val) {
    case B0: {
            ps = new GdrStdfField();
            gdr_variant v = 0;
            ps->set(v, curpos, 1, 1, B0);
            break;
        }
    case U1: {
            NmField<unsigned int, 1> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, U1);
            break;
        }
    case U2: {
            NmField<unsigned int, 2> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, U2);
            break;
        }
    case U4: {
            NmField<unsigned int, 4> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, U4);
            break;
        }
    case I1: {
            NmField<signed int, 1> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, I1);
            break;
        }
    case I2: {
            NmField<signed int, 2> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, I2);
            break;
        }
    case I4: {
            NmField<signed int, 4> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, I4);
            break;
        }
    case R4: {
            NmField<float, 4> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, R4);
            break;
        }
    case R8: {
            NmField<double, 8> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, R8);
            break;
        }
    case CN: {
            CnField<string> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, CN);
            break;
        } /*
    case BN: {
            CnField<CharArray, 1> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, BN);
            break;
        } */
    case DN: {
            NmField<int, 2> nm(buf+curpos+1, en);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, DN);
            break;
        } /*
    case N1: {
            NmArrField<Nibble,1> nm(buf+curpos+1, en, 1);
            ps = new GdrStdfField();
            gdr_variant v = nm.getData();
            ps->set(v, curpos, nm.getByteCount(), nm.getTotalByteCount()+1, N1);
            break;
        } */
    default:  {
            // do nothing
            break;
        }
    };

    return ps;
}

